package com.suncky.frame.utils;

import android.app.Person;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * gson 相关工具
 */
public class GsonUtils {
    private static Gson gson;

    public static GsonBuilder getDefaultGsonBuilder() {
        return new GsonBuilder().registerTypeAdapterFactory(TypeAdapters.newFactory(int.class, Integer.class, INTEGER))
                .registerTypeAdapterFactory(TypeAdapters.newFactory(long.class, Long.class, LONG))
                .registerTypeAdapterFactory(TypeAdapters.newFactory(String.class, STRING))
                .registerTypeAdapterFactory(TypeAdapters.newFactory(new TypeToken<List<String>>() {}, new ListTypeAdapter<>()))
                .registerTypeAdapterFactory(TypeAdapters.newFactory(new TypeToken<List<Integer>>() {}, new ListTypeAdapter<>()))
                .registerTypeAdapterFactory(TypeAdapters.newFactory(new TypeToken<List<Long>>() {}, new ListTypeAdapter<>()))
                .registerTypeAdapterFactory(TypeAdapters.newFactory(new TypeToken<List<Float>>() {}, new ListTypeAdapter<>()))
                .registerTypeAdapterFactory(TypeAdapters.newFactory(new TypeToken<List<Boolean>>() {}, new ListTypeAdapter<>()))
                //解决json转Map<Key,Object>时，long和int默认为double类型的问题
                .registerTypeAdapterFactory(ObjectTypeAdapter.FACTORY)
                ;
    }

    public static Gson getGson() {
        if (gson == null) {
            gson = getDefaultGsonBuilder().create();
        }
        return gson;
    }

    public static void setGson(Gson newGson) {
        gson = newGson;
    }

    /**
     * 获取参数化类型的type实例
     * @param cls 原始类型
     * @param pCls 参数类型
     * @return
     * @param <T>
     */
    public static <T> Type getParameterizedType(Class<T> cls, Class... pCls) {
        return TypeToken.getParameterized(cls, pCls).getType();
    }

    /**
     * 获取嵌套参数化类型的type实例
     * @param cls 原始类型
     * @param pCls 参数类型，从外层依次传递
     * @return
     */
    public static <T> Type getNestedParameterizedType(Class<T> cls, Class... pCls) {
        if (pCls.length == 1) {
            return getParameterizedType(cls, pCls);
        }
        Type type = TypeToken.getParameterized(pCls[pCls.length-2], pCls[pCls.length-1]).getType();
        if (pCls.length > 2) {
            for (int i = pCls.length - 3; i >= 0; --i) {
                type = TypeToken.getParameterized(pCls[i], type).getType();
            }
        }

        return TypeToken.getParameterized(cls,type).getType();
    }

    /**
     * 对象转为json串，不含null
     * @param obj
     * @return
     */
    public static String objectToJson(Object obj) {
        return getGson().toJson(obj);
    }


    /**
     * json字符串转化为对象
     * @param cls 对象的类型class
     * @param json json数据
     * @return
     * @param <T>
     */
    public static <T> T jsonToObject(Class<T> cls, String json) {
        return getGson().fromJson(json, cls);
    }

    /**
     * json字符串转化为对象
     * @param cls 对象的类型class
     * @param json JsonElement对象
     * @return
     * @param <T>
     */
    public static <T> T jsonToObject(Class<T> cls, JsonElement json) {
        return getGson().fromJson(json, cls);
    }

    /**
     * json字符串转化为List
     * @param cls List元素类型的Class对象，只能是引用类型
     * @param json json字符串
     * @return
     */
    public static <T> List<T> jsonToList(Class<T> cls, String json) {
        return getGson().fromJson(json, TypeToken.getParameterized(List.class, cls).getType());
    }

    /**
     * JsonElement对象转化为List
     * @param cls List元素类型的Class对象，只能是引用类型
     * @param json JsonElement对象
     * @return
     */
    public static <T> List<T> jsonToList(Class<T> cls, JsonElement json) {
        return getGson().fromJson(json, TypeToken.getParameterized(List.class, cls).getType());
    }

    /**
     * json转化为数组
     * @param cls 数组类型的Class对象，可以是基本类型和引用类型
     * @param json json字符串
     * @return
     */
    public static <T> T[] jsonToArray(Class<T[]> cls, String json) {
        return getGson().fromJson(json,TypeToken.get(cls).getType());
    }

    /**
     * json转化为数组
     * @param cls 数组元素类型的Class对象，只能是引用类型
     * @param json json字符串
     * @return
     */
    public static <T> T[] jsonToObjectArray(Class<T> cls, String json) {
        return getGson().fromJson(json, TypeToken.getArray(cls).getType());
    }

    /**
     * json转为带有类型参数的对象
     * @param json json数据
     * @param cls 原始类型
     * @param pCls 参数类型
     * @return 参数化类型实例
     */
    public static <T> T jsonToParamTypeObject(String json, Class<T> cls, Class... pCls) {
        return getGson().fromJson(json, getParameterizedType(cls,pCls));
    }

    /**
     * json转为带有嵌套类型参数的对象
     * @param json json数据
     * @param cls 原始类型
     * @param pCls 参数类型，从外层依次传递
     * @return
     * @param <T>
     */
    public static <T> T jsonToNestedParamTypeObject(String json, Class<T> cls, Class... pCls) {
        return getGson().fromJson(json, getNestedParameterizedType(cls,pCls));
    }


    /**
     * 对象转化为JsonObject实例
     * @param o
     * @return
     */
    public static JsonObject toJsonObject(Object o) {
        JsonElement e = getGson().toJsonTree(o);
        if (e.isJsonObject()) {
            return e.getAsJsonObject();
        }
        return null;
    }

    /**
     * List转化为JsonArray实例
     *
     * @param o
     * @return
     */
    public static <T> JsonArray toJsonArray(List<T> o) {
        JsonElement e = getGson().toJsonTree(o);
        if (e.isJsonArray()) {
            return e.getAsJsonArray();
        }
        return null;
    }

    /**
     * List转化为JsonArray实例
     *
     * @param o
     * @return
     */
    public static <T> JsonArray toJsonArray(T[] o) {
        JsonElement e = getGson().toJsonTree(o);
        if (e.isJsonArray()) {
            return e.getAsJsonArray();
        }
        return null;
    }

    /**
     * List转为json
     *
     * @param o
     * @return
     */
    public static  <T> String listToJson(List <T> o) {
        JsonArray array = toJsonArray(o);
        if (array == null) {
            return null;
        }
        return array.toString();
    }

    /**
     * 数组转为json
     *
     * @param o
     * @return
     */
    public static <T> String arrayToJson(T[] o) {
        JsonArray array = toJsonArray(o);
        if (array == null) {
            return null;
        }
        return array.toString();
    }

    /**
     * json转为map
     * @param json json数据
     * @return
     */
    public static <V> Map<String, V> jsonToMap(String json) {
        return getGson().fromJson(json, new TypeToken<Map<String, V>>() {
        }.getType());
    }

    /**
     * 对象转为map
     * @param o 要转换的对象
     * @return
     */
    public static <V> Map<String, V> objectToMap(Object o) {
        return jsonToMap(objectToJson(o));
    }


    //int类型的解析器
    public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
        @Override
        public Number read(JsonReader in) throws IOException {
            if (in.peek() == JsonToken.NULL) {
                in.nextNull();
                return null;
            }
            try {
                return in.nextInt();
            } catch (NumberFormatException e) {
                //这里解析int出错，那么捕获异常并且返回默认值，因为nextInt出错中断了方法，没有完成位移，所以调用nextString()方法完成位移。
                in.nextString();
                return Integer.MAX_VALUE;
            }
        }

        @Override
        public void write(JsonWriter out, Number value) throws IOException {
            out.value(value);
        }
    };
    //long类型的解析器
    public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() {
        @Override
        public Number read(JsonReader in) throws IOException {
            if (in.peek() == JsonToken.NULL) {
                in.nextNull();
                return null;
            }
            try {
                return in.nextLong();
            } catch (Exception e) {
                in.nextString();
            }
            return Long.MAX_VALUE;
        }

        @Override
        public void write(JsonWriter out, Number value) throws IOException {
            out.value(value);

        }
    };

    //String类型的解析器
    public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {

        @Override
        public String read(JsonReader in) throws IOException {
            //为增加数据类型的容错, 针对不同的数据类型进行处理, 返回相应的字符串.
            switch (in.peek()) {
                case BEGIN_ARRAY://如果实际数据类型为数组,
                    List<Object> list = new ArrayList<>();
                    in.beginArray();
                    while (in.hasNext()) {
                        list.add(read(in));
                    }
                    in.endArray();
                    return list.toString();
                case BEGIN_OBJECT:
                    Map<String, Object> map = new LinkedTreeMap<>();
                    in.beginObject();
                    while (in.hasNext()) {
                        map.put(in.nextName(), read(in));
                    }
                    in.endObject();
                    return map.toString();
                case NULL:
                    in.nextNull();
                    return null;
                default:
                    return in.nextString();
            }
        }

        @Override
        public void write(JsonWriter out, String value) throws IOException {
            out.value(value);

        }
    };

    public static final class ListTypeAdapter<T> extends TypeAdapter<List<T>>{

        @SuppressWarnings("unchecked")
        @Override
        public void write(JsonWriter out, List<T> value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }

            TypeAdapter<List<T>> typeAdapter = (TypeAdapter<List<T>>)gson.getAdapter(value.getClass());
            typeAdapter.write(out, value);
        }

        @SuppressWarnings("unchecked")
        @Override
        public List<T> read(JsonReader in) throws IOException {
            //为增加数据类型的容错, 针对不同的数据类型进行处理
            LogUtils.i("ListTypeAdapter::read JsonReader="+in.toString());
            switch (in.peek()) {
                case BEGIN_ARRAY:
                    List<T> listArray = new ArrayList<>();
                    in.beginArray();
                    while (in.hasNext()) {
                        listArray.add((T) jsonRead(in));
                    }
                    in.endArray();
                    return listArray;
                case BEGIN_OBJECT:
                    in.beginObject();
                    while (in.hasNext()) {
                        in.nextName();
                        read(in);
                    }
                    in.endObject();
                    return null;
                case NULL:
                    in.nextNull();
                    return null;
                default:
                    in.nextString();
                    return null;
            }
        }
    }

    //重写ObjectTypeAdapter类，修改NUMBER类型默认返回double的问题
    public static final class ObjectTypeAdapter extends TypeAdapter<Object> {
        public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
            @SuppressWarnings("unchecked")
            @Override
            public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
                if (type.getRawType() == Object.class || type.getRawType() == Map.class) {
                    return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
                }
                return null;
            }
        };

        private final Gson gson;

        ObjectTypeAdapter(Gson gson) {
            this.gson = gson;
        }

        @Override
        public Object read(JsonReader in) throws IOException {
            return jsonRead(in);
        }

        @SuppressWarnings("unchecked")
        @Override
        public void write(JsonWriter out, Object value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }

            TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
            if (typeAdapter instanceof com.google.gson.internal.bind.ObjectTypeAdapter) {
                out.beginObject();
                out.endObject();
                return;
            }

            typeAdapter.write(out, value);
        }
    }


    public static Object jsonRead(JsonReader in) throws IOException {
        JsonToken token = in.peek();
        switch (token) {
            case BEGIN_ARRAY:
                List<Object> list = new ArrayList<>();
                in.beginArray();
                while (in.hasNext()) {
                    list.add(jsonRead(in));
                }
                in.endArray();
                return list;
            case BEGIN_OBJECT:
                Map<String, Object> map = new LinkedTreeMap<>();
                in.beginObject();
                while (in.hasNext()) {
                    map.put(in.nextName(), jsonRead(in));
                }
                in.endObject();
                return map;
            case STRING:
                return in.nextString();
            case NUMBER:
                double dNum = in.nextDouble();
                long lNum = (long) dNum;
                if (lNum == dNum) {
                    return lNum;
                }
                return dNum;
            case BOOLEAN:
                return in.nextBoolean();
            case NULL:
                in.nextNull();
                return null;
            default:
                throw new IllegalStateException();
        }
    }
}
